package sm.hibernate.type;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import sm.codec.type.Struct;
import sm.def.Type;
import sm.util.SMMap;

public class RCTypeHelper {

	public static final String EMPTY_STRING_IN_JAVA = "";

	public static final String EMPTY_STRNIG_IN_DB = " ";

	public static Object dbStringToJava(Object str) {
		if (EMPTY_STRNIG_IN_DB.equals(str)) {
			return EMPTY_STRING_IN_JAVA;
		}
		return str;
	}

	public static Object javaStringToDB(Object str) {
		if (EMPTY_STRING_IN_JAVA.equals(str)) {
			return EMPTY_STRNIG_IN_DB;
		}
		return str;
	}

	/**
	 * convert a RC structure to a SMMap
	 * 
	 * @param values
	 * @param typedef
	 * @return
	 */
	public static SMMap structureToMap(List<Object> values,
			final Map<String, Object> typedef) {
		SMMap structure = new SMMap();
		int size = values.size();
		int i = 0;
		for (Entry<String, Object> entry : typedef.entrySet()) {
			String key = entry.getKey();
			Object type = entry.getValue();
			if (i < size) {
				Object value = values.get(i);
				if (value instanceof Struct) { // composite type
					Struct struct = (Struct) value;
					List<Object> subValues = struct.getValue();
					if (struct.getType() == Type.STRUCTURE_TYPE) { // structure
						if (type instanceof Map) {
							Map<String, Object> fieldsType = (Map<String, Object>) type;
							SMMap subStructure = structureToMap(subValues,
									fieldsType);
							structure.put(key, subStructure);
						} else {
							structure.put(key, struct);
						}
					} else if (struct.getType() == Type.ARRAY_TYPE) { // array
						if (type instanceof List) {
							Object fieldsType = ((List<Object>) type).get(0);
							List<Object> subArray = arrayToList(subValues,
									fieldsType);
							structure.put(key, subArray);
						} else {
							structure.put(key, struct);
						}
					}
				} else { // primitive type or expression
					structure.put(key, value);
				}
			} else {
				structure.put(key, null);
			}
			i++;
		}
		return structure;
	}

	/**
	 * convert a RC array to ArrayList
	 * 
	 * @param values
	 * @param type
	 * @return
	 */
	public static List<Object> arrayToList(List<Object> values,
			final Object type) {
		List<Object> array = new ArrayList<Object>();
		for (Object value : values) {
			if (value instanceof Struct) {// composite type
				Struct struct = (Struct) value;
				List<Object> subValues = struct.getValue();
				if (struct.getType() == Type.STRUCTURE_TYPE) { // structure
					if (type instanceof Map) {
						Map<String, Object> fieldType = (Map<String, Object>) type;
						Map<String, Object> fields = structureToMap(subValues,
								fieldType);
						array.add(fields);
					} else {
						array.add(struct);
					}
				} else if (struct.getType() == Type.ARRAY_TYPE) { // array
					if (type instanceof List) {
						List<Object> fieldType = (List<Object>) type;
						List<Object> fields = arrayToList(subValues, fieldType);
						array.add(fields);
					} else {
						array.add(struct);
					}
				}
			} else {// primitive type or expression
				array.add(value);
			}
		}
		return array;
	}

	private static Object nullValue(Object type) {
		Object nullValue = null;
		if (type instanceof Map) {
			nullValue = new Struct(Collections.EMPTY_LIST, Type.STRUCTURE_TYPE);
		} else if (type instanceof List) {
			nullValue = new Struct(Collections.EMPTY_LIST, Type.ARRAY_TYPE);
		} else {
			int primitiveType = ((Double) type).intValue();
			nullValue = Type.getType(primitiveType).getNullValue();
		}
		return nullValue;
	}

	/**
	 * convert a SMMap to RC structure
	 * 
	 * @param structure
	 * @param typedef
	 * @return
	 */
	public static List<Object> mapToStructure(SMMap structure,
			Map<String, Object> typedef) {
		List<Object> values = new ArrayList<Object>();
		for (Entry<String, Object> entry : typedef.entrySet()) {
			String key = entry.getKey();
			Object type = entry.getValue();
			Object value = structure.get(key);
			if (value == null) {
				Object nullValue = nullValue(type);
				values.add(nullValue);
			} else {
				if (value instanceof SMMap) { // structure
					SMMap structureValue = (SMMap) value;
					if (type instanceof Map) {
						Map<String, Object> structureType = (Map<String, Object>) type;
						List<Object> subValues = mapToStructure(structureValue,
								structureType);
						Struct struct = new Struct(subValues,
								Type.STRUCTURE_TYPE);
						values.add(struct);
					} else {
						Object nullValue = nullValue(type);
						values.add(nullValue);
					}
				} else if (value instanceof List) { // array)
					List<Object> arrayValue = (List<Object>) value;
					if (type instanceof List) {
						Object arrayType = ((List<Object>) type).get(0);
						List<Object> subValues = listToArray(arrayValue,
								arrayType);
						Struct struct = new Struct(subValues, Type.ARRAY_TYPE);
						values.add(struct);
					} else {
						Object nullValue = nullValue(type);
						values.add(nullValue);
					}
				} else { // primitive type or expression
					values.add(value);
				}
			}
		}
		return values;
	}

	/**
	 * convert a ArrayList to RC array
	 * 
	 * @param array
	 * @param type
	 * @return
	 */
	public static List<Object> listToArray(List<Object> array, Object type) {
		List<Object> values = new ArrayList<Object>();
		for (Object value : array) {
			if (value == null) {
				Object nullValue = nullValue(type);
				values.add(nullValue);
			} else {
				if (value instanceof SMMap) {// structure
					SMMap structureValue = (SMMap) value;
					if (type instanceof Map) {
						Map<String, Object> structureType = (Map<String, Object>) type;
						List<Object> subValues = mapToStructure(structureValue,
								structureType);
						Struct struct = new Struct(subValues,
								Type.STRUCTURE_TYPE);
						values.add(struct);
					} else {
						Object nullValue = nullValue(type);
						values.add(nullValue);
					}
				} else if (value instanceof List) { // array
					List<Object> arrayValue = (List<Object>) value;
					if (type instanceof List) {
						Object arrayType = ((List<Object>) type).get(0);
						List<Object> subValues = listToArray(arrayValue,
								arrayType);
						Struct struct = new Struct(subValues, Type.ARRAY_TYPE);
						values.add(struct);
					} else {
						Object nullValue = nullValue(type);
						values.add(nullValue);
					}
				} else { // primitive type or expression
					values.add(value);
				}
			}

		}
		return values;
	}
}
